﻿//
// Copyright (c) 2024 BPergaofeng
//
#include <bperpush/BPerRtcPublish.h>
#include <bperstream/BPerStreamType.h>
#include <bperstream/BPerStreamCapture.h>

#include <bperutil/sys/BPerLog.h>

#include <bperavutil/video/BPerNalu.h>
#include <bperavutil/video/BPerMeta.h>
#include <bperavutil/video/BPerVideoEncoderMeta.h>


BPerRtcPublish::BPerRtcPublish(BPerContext *pcontext) {
    m_context = pcontext;

    m_in_videoBuffer = NULL;
    m_in_audioBuffer = NULL;
    m_isStart = 0;
    m_isConvert = 0;
    m_vmd = NULL;
    m_audioEncoderType = m_context->avinfo.audio.audioEncoderType;
    m_netState = 1;
    m_isInit = 0;
    isPublished = 0;

    m_transType = BPer_Webrtc;
    notifyState = 0;


}

BPerRtcPublish::~BPerRtcPublish() {
    if (m_isConvert) {
        stop();
        while (m_isStart) {
            bPer_usleep(1000);
        }
    }
    m_context = NULL;
    m_in_videoBuffer = NULL;
    m_in_audioBuffer = NULL;
    m_vmd = NULL;
}


int32_t BPerRtcPublish::connectServer(int32_t puid) {
    return m_pushs.back()->connectSfuServer();
}


int32_t BPerRtcPublish::reconnectMediaServer() {
    return m_pushs.back()->connectSfuServer();
}

int32_t BPerRtcPublish::publishMsg(BPerFrame *msgFrame) {

    if (m_pushs.size() > 0) {
        msgFrame->uid = m_pushs.front()->streamConfig->uid;
        return m_pushs.front()->on_message(msgFrame);
    }
    return 1;
}

int32_t BPerRtcPublish::receiveMsg(BPerFrame *msgFrame) {

    return BPer_Ok;
}

int32_t BPerRtcPublish::init(int32_t nettype, char *server, int32_t pport,
                             char *app, char *stream) {

    int32_t ret = 0;
    BPerStreamConfig streamconfig;
    memset(&streamconfig, 0, sizeof(BPerStreamConfig));
    strcpy(streamconfig.app, app);
    streamconfig.direction = BPerSendonly;

    strcpy(streamconfig.remoteIp, server);
    streamconfig.remotePort = pport;

    strcpy(streamconfig.stream, stream);
    streamconfig.uid = 0;

    streamconfig.localPort = m_context->avinfo.rtc.rtcLocalPort;

    memcpy(&streamconfig.rtcCallback, &m_context->rtcCallback, sizeof(BPerRtcCallback));

    BPerPeerConnection2 *sh = new BPerPeerConnection2(&m_context->avinfo, &streamconfig);
    sh->init();
    sh->addAudioTrack(BPer_AED_OPUS);
    sh->addVideoTrack(BPer_VED_H264);
    sh->addTransceiver(streamconfig.direction);
    m_pushs.push_back(sh);


    if (sh->isConnected()) return BPer_Ok;

    ret = m_pushs.back()->connectSfuServer();

    if (ret) return ret;

    bPer_reindex(m_in_audioBuffer);
    bPer_reindex(m_in_videoBuffer);
    return BPer_Ok;

}

int32_t BPerRtcPublish::init(char *url) {

    int32_t ret = 0;
    BPerStreamConfig streamconfig;
    memset(&streamconfig, 0, sizeof(BPerStreamConfig));
    streamconfig.direction = BPerSendonly;
    streamconfig.uid = 0;

    streamconfig.localPort = m_context->avinfo.rtc.rtcLocalPort;

    memcpy(&streamconfig.rtcCallback, &m_context->rtcCallback, sizeof(BPerRtcCallback));

    BPerPeerConnection2 *sh = new BPerPeerConnection2(&m_context->avinfo, &streamconfig);
    sh->init();
    sh->addAudioTrack(BPer_AED_OPUS);
    sh->addVideoTrack(BPer_VED_H264);
    sh->addTransceiver(streamconfig.direction);
    m_pushs.push_back(sh);

    ret = m_pushs.back()->connectWhipServer(url);

    if (ret) return ret;

    bPer_reindex(m_in_audioBuffer);
    bPer_reindex(m_in_videoBuffer);
    return BPer_Ok;

}

int32_t BPerRtcPublish::connectMediaServer() {
    if (m_pushs.size() > 0) return BPer_Ok;

    return BPer_Ok;
}

int32_t BPerRtcPublish::disConnectMediaServer() {
    if (m_pushs.size() > 0) {
        bPer_delete(m_pushs.back());
        m_pushs.clear();
    }
    return BPer_Ok;
}

void BPerRtcPublish::stop() {
    m_isConvert = 0;
}

void BPerRtcPublish::run() {
    m_isStart = 1;
    startLoop();
    m_isStart = 0;
}

void BPerRtcPublish::setInAudioList(BPerAudioEncoderBuffer *pbuf) {
    m_in_audioBuffer = pbuf;
}

void BPerRtcPublish::setInVideoList(BPerVideoEncoderBuffer *pbuf) {
    m_in_videoBuffer = pbuf;
}

void BPerRtcPublish::setInVideoMetaData(BPerVideoMeta *pvmd) {
    m_vmd = pvmd;
}

void BPerRtcPublish::startLoop() {

    isPublished = 0;
    m_isConvert = 1;

    BPerStreamCapture data;
    memset(&data, 0, sizeof(BPerStreamCapture));
    bPer_create_streamCapture(&data);
    BPerFrame audioFrame;
    BPerFrame videoFrame;
    memset(&audioFrame, 0, sizeof(BPerFrame));
    memset(&videoFrame, 0, sizeof(BPerFrame));
    data.initAudio(data.context, m_context->avinfo.sys.transType, m_context->avinfo.audio.sample,
                   m_context->avinfo.audio.channel,
                   (BPerAudioCodec) m_context->avinfo.audio.audioEncoderType);
    data.initVideo(data.context, m_context->avinfo.sys.transType);
    BPerVideoCodec videoType = (BPerVideoCodec) m_context->avinfo.video.videoEncoderType;

    int32_t ret = BPer_Ok;
    isPublished = 1;
    notifyState = 1;
    BPerVideoMeta *vmd = NULL;
    if (m_context->avinfo.enc.createMeta == 0) {
        vmd = (BPerVideoMeta *) calloc(sizeof(BPerVideoMeta), 1);
    }

    BPerH264NaluData nalu;
    BPerPeerConnection2 *stream = NULL;
    while (m_isConvert == 1) {


        if ((m_in_videoBuffer && m_in_videoBuffer->size() == 0)
            && (m_in_audioBuffer && m_in_audioBuffer->size() == 0)) {
            bPer_usleep(2000);
            continue;
        }
        if (m_pushs.size() == 0) {
            bPer_usleep(500);
            continue;
        }
        stream = m_pushs.back();

        if (stream->isConnected()) {
            if (notifyState && m_transType < BPer_Webrtc) {
                if (m_context) m_context->streams->sendRequest(0, 0, BPer_Req_Connected);
                notifyState = 0;
            }

        } else {
            bPer_usleep(500);
            continue;
        }

        if (m_in_audioBuffer && m_in_audioBuffer->size() > 0) {

            audioFrame.payload = m_in_audioBuffer->getAudioRef(&audioFrame);
            data.setAudioData(data.context, &audioFrame);

            //for (i = 0; i < m_pushs.size(); i++) {
            ret = stream->on_audio(data.getAudioFrame(data.context));
            if (ret && !stream->isConnected()) {
                stream->close();
                //BPer_post_message(BPerM_Sys_PushMediaServerError,m_pushs.back()->m_uid,NULL);
            }
            //}
        }

        if (m_in_videoBuffer && m_in_videoBuffer->size() > 0) {

            videoFrame.payload = m_in_videoBuffer->getEVideoRef(&videoFrame);

            if (videoFrame.frametype == BPER_FrameType_I) {

                if (m_vmd) {
                    data.setVideoMeta(data.context, m_vmd->livingMeta.buffer,
                                      m_vmd->livingMeta.bufLen, videoType);
                    //if (ret)	continue;
                } else {
                    if (!vmd->isInit) {
                        if (videoType == BPer_VED_H264) {
                            bPer_createH264Meta(vmd, &videoFrame);
                            bPer_getConfig_Flv_H264(&vmd->mp4Meta,
                                                    vmd->livingMeta.buffer,
                                                    &vmd->livingMeta.bufLen);
                        } else if (videoType == BPer_VED_H265) {
                            bPer_createH265Meta(vmd, &videoFrame);
                            bPer_getConfig_Flv_H265(&vmd->mp4Meta,
                                                    vmd->livingMeta.buffer,
                                                    &vmd->livingMeta.bufLen);
                        }
                    }

                    data.setVideoMeta(data.context, vmd->livingMeta.buffer, vmd->livingMeta.bufLen, videoType);


                }
                data.setVideoFrametype(data.context, BPER_FrameType_Spspps);
                data.setMetaTimestamp(data.context, videoFrame.pts);
                ret = stream->on_video(data.getVideoFrame(data.context));


                if (!m_context->avinfo.enc.createMeta) {

                    memset(&nalu, 0, sizeof(BPerH264NaluData));
                    if (videoType == BPer_VED_H264)
                        bPer_parseH264Nalu(&videoFrame, &nalu);
                    else
                        bPer_parseH265Nalu(&videoFrame, &nalu);


                    if (nalu.keyframePos > -1) {
                        videoFrame.payload += nalu.keyframePos + 4;
                        videoFrame.nb -= (nalu.keyframePos + 4);

                    } else {
                        videoFrame.payload = NULL;
                        continue;
                    }
                }

            }

            data.setVideoData(data.context, &videoFrame, videoType);
            ret = stream->on_video(data.getVideoFrame(data.context));

            if (ret && !stream->isConnected()) {
                stream->close();

            }

        }            //end
    }
    isPublished = 0;
    bPer_destroy_streamCapture(&data);
    bPer_free(vmd);
}
